
推导过程将每个参数进行匹配，以确定模板参数的值。当对可变参数模板执行模板参数推导时，参数之间不再具有一一对应的关系，因为一个参数包可以匹配多个参数。这种情况下，同一个参数包(P)匹配多个参数(A)，每次匹配都会为P中的模板参数包产生额外的值:

\begin{lstlisting}[style=styleCXX]
template<typename First, typename... Rest>
void f(First first, Rest... rest);

void g(int i, double j, int* k)
{
	f(i, j, k); // deduces First to int, Rest to {double, int*}
}
\end{lstlisting}

第一个函数参数的推导很简单，不涉及参数包。第二个函数参数rest是一个函数参数包，类型是一个包扩展(Rest…)，模式是Rest类型:该模式用作P，与第二个和第三个调用参数的类型A进行比较。当与第一个A(double类型)比较时，模板参数包Rest中的第一个值推导为double。类似地，当与第二个这样的A(类型为int*)比较时，模板参数包Rest中的第二个值推导为int*。因此，推导决定了参数包Rest的值是序列\{double, int*\}。将该推导结果和第一个函数参数的推导结果结合，替换为函数类型void(int, double, int*)，与调用点的参数类型匹配。

因为函数参数包的推导使用展开模式进行比较，所以模式可以随意，并且可以从每个参数类型确定多个模板参数和参数包的值。考虑h1()和h2()函数的推导行为:

\begin{lstlisting}[style=styleCXX]
template<typename T, typename U> class pair { };

template<typename T, typename... Rest>
	void h1(pair<T, Rest> const&...);

template<typename... Ts, typename... Rest>
	void h2(pair<Ts, Rest> const&...);

void foo(pair<int, float> pif, pair<int, double> pid,
pair<double, double> pdd)
{
	h1(pif, pid); // OK: deduces T to int, Rest to {float, double}
	h2(pif, pid); // OK: deduces Ts to {int, int}, Rest to {float, double}
	h1(pif, pdd); // ERROR: T deduced to int from the 1st arg, but to double from the 2nd
	h2(pif, pdd); // OK: deduces Ts to {int, double}, Rest to {float, double}
}
\end{lstlisting}

对于h1()和h2()，P是一个引用类型，为引用的非限定版本(分别为pair<T, Rest>或pair<Ts, Rest>)，用于对每个参数类型进行推导。所有参数都是类模板pair的特化，因此需要比较模板参数。对于h1()，第一个模板参数(T)不是一个参数包，因此其值是针对每个参数可以进行独立推导。如果类型推导不同，如第二次调用h1()，则推导失败。对第二个在h1()和h2()的pair模板参数Rest、以及h2()的第一个模板实参Ts来说，推导会根据A的每个参数类型来确定模板参数包的值。

参数包的推导不限于来自函数参数包的参数对，当包扩展位于函数参数列表或模板参数列表的末尾时，就会使用这种推导。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}如果包展开发生在函数参数列表或模板参数列表的位置，则该包展开不可推导上下文。
\end{tcolorbox}

例如，Tuple类型上的两个类似操作:

\begin{lstlisting}[style=styleCXX]
template<typename... Types> class Tuple { };

template<typename... Types>
bool f1(Tuple<Types...>, Tuple<Types...>);

template<typename... Types1, typename... Types2>
bool f2(Tuple<Types1...>, Tuple<Types2...>);

void bar(Tuple<short, int, long> sv,
		Tuple<unsigned short, unsigned, unsigned long> uv)
{
	f1(sv, sv); // OK: Types is deduced to {short, int, long}
	f2(sv, sv); // OK: Types1 is deduced to {short, int, long},
				// Types2 is deduced to {short, int, long}
	f1(sv, uv); // ERROR: Types is deduced to {short, int, long} from the 1st arg, but
				// to {unsigned short, unsigned, unsigned long} from the 2nd
	f2(sv, uv); // OK: Types1 is deduced to {short, int, long},
				// Types2 is deduced to {unsigned short, unsigned, unsigned long}
}
\end{lstlisting}

f1()和f2()中，模板参数包通过比较嵌在Tuple类型中的包展开模式(例如，类型为h1())与调用参数提供Tuple类型的每个模板参数来推导的，依次为相应的模板参数包推导出相应的值。函数f1()在两个函数参数中使用相同的模板参数包Types，确保只有两个函数调用参数，具有与其类型相同的Tuple特化时，推导才能成功。另一方面，函数f2()在其每个函数参数中为Tuple类型使用不同的参数包，因此函数调用参数的类型可以不同——只要两者都是Tuple的特化即可。

\subsubsubsection{15.5.1\hspace{0.2cm}字面量操作符模板}

文字操作符模板的参数以一种独特的方式确定。下面的例子说明了这一点:

\begin{lstlisting}[style=styleCXX]
template<char...> int operator "" _B7(); // #1
...
int a = 121_B7; // #2
\end{lstlisting}

这里，\#2的初始化式包含用户定义的字面量，转换成对字面操作符模板\#2的调用，其中包含模板参数列表<'1'，'2'，'1'>。因此，文字操作符的实现：

\begin{lstlisting}[style=styleCXX]
template<char... cs>
int operator"" _B7()
{
	std::array<char,sizeof...(cs)> chars{cs...}; // initialize array of passed chars
	for (char c : chars) { // and use it (print it here)
		std::cout << "’" << c << "’ ";
	}
	std::cout << ’\n’;
	return ...;
}
\end{lstlisting}

会为121.5\_B7输出'1' '2' '1' '.' '5'。

这种技术只支持那些没有后缀也有效的数字字面值。例如:

\begin{lstlisting}[style=styleCXX]
auto b = 01.3_B7; // OK: deduces <’0’, ’1’, ’.’, ’3’>
auto c = 0xFF00_B7; // OK: deduces <’0’, ’x’, ’F’, ’F’, ’0’, ’0’>
auto d = 0815_B7; // ERROR: 8 is no valid octal literal
auto e = hello_B7; // ERROR: identifier hello_B7 is not defined
auto f = "hello"_B7; // ERROR: literal operator _B7 does not match
\end{lstlisting}

请参阅第25.6节，了解该特性的应用：在编译时计算整数字面值。






















